package com.cardone.common.util;

import lombok.extern.slf4j.*;

import javax.crypto.*;
import java.io.*;
import java.security.*;

@Slf4j
public class CipherUtils {
    /**
     * 使用AES对文件进行加密和解密
     */
    private static String type = "AES";

    private CipherUtils() {
    }

    /**
     * 把文件srcFile解密后存储为destFile
     *
     * @param srcFile    解密前的文件
     * @param destFile   解密后的文件
     * @param privateKey 密钥
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static void decrypt(final String srcFile, final String destFile, final String privateKey) throws GeneralSecurityException, IOException {
        final Key key = CipherUtils.getKey(privateKey);
        final Cipher cipher = Cipher.getInstance(CipherUtils.type + "/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);

        try (FileInputStream fis = new FileInputStream(srcFile)) {
            try (FileOutputStream fos = new FileOutputStream(CipherUtils.mkdirFiles(destFile))) {
                CipherUtils.crypt(fis, fos, cipher);
            }
        }
    }

    /**
     * 生成指定字符串的密钥
     *
     * @param secret 要生成密钥的字符串
     * @return secretKey 生成后的密钥
     * @throws GeneralSecurityException
     */
    private static Key getKey(final String secret) throws GeneralSecurityException {
        final KeyGenerator kgen = KeyGenerator.getInstance(CipherUtils.type);
        final SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
        secureRandom.setSeed(secret.getBytes());
        kgen.init(128, secureRandom);
        return kgen.generateKey();
    }

    /**
     * 根据filePath创建相应的目录
     *
     * @param filePath 要创建的文件路经
     * @return file 文件
     * @throws IOException
     */
    private static File mkdirFiles(final String filePath) throws IOException {
        final File file = new File(filePath);

        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }

        file.createNewFile();

        return file;
    }

    /**
     * 加密解密流
     *
     * @param in     加密解密前的流
     * @param out    加密解密后的流
     * @param cipher 加密解密
     * @throws IOException
     * @throws GeneralSecurityException
     */
    private static void crypt(final InputStream in, final OutputStream out, final Cipher cipher) throws IOException, GeneralSecurityException {
        final int blockSize = cipher.getBlockSize() * 1000;
        final int outputSize = cipher.getOutputSize(blockSize);

        final byte[] inBytes = new byte[blockSize];
        byte[] outBytes = new byte[outputSize];

        int inLength;

        while ((inLength = in.read(inBytes)) == blockSize) {
            final int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
            out.write(outBytes, 0, outLength);
        }

        if (inLength > 0) {
            outBytes = cipher.doFinal(inBytes, 0, inLength);
        } else {
            outBytes = cipher.doFinal();
        }

        out.write(outBytes);
    }

    /**
     * 把文件srcFile加密后存储为destFile
     *
     * @param srcFile    加密前的文件
     * @param destFile   加密后的文件
     * @param privateKey 密钥
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static void encrypt(final String srcFile, final String destFile, final String privateKey) throws GeneralSecurityException, IOException {
        final Key key = CipherUtils.getKey(privateKey);

        final Cipher cipher = Cipher.getInstance(CipherUtils.type + "/ECB/PKCS5Padding");

        cipher.init(Cipher.ENCRYPT_MODE, key);

        try (FileInputStream fis = new FileInputStream(srcFile)) {
            try (FileOutputStream fos = new FileOutputStream(CipherUtils.mkdirFiles(destFile))) {
                CipherUtils.crypt(fis, fos, cipher);

                fos.flush();
            }
        }
    }
}
